#include "game_transam.h"

using namespace std;


const float Car::FUEL_MAX(60.0f);

//innitialise a car back to it's defaults
void Car::SetupCar()
{
	int carwidth,carheight;
	angle=0;acc=0;vel=0.0;
	decel=0.5;
	car=NULL;
	Handbrake=false;
	Mph=0;
	Fuel=Car::FUEL_MAX;
	Temperature=0.0;
	SimpleAcceleration=false;
	MilesDriven=0.0;
	
	TemperatureInc=3.333/GameConfiguration->CapsSystem.fps;
	TemperatureDec=5.0/GameConfiguration->CapsSystem.fps;
	FuelUnit=(1.0/(float)Car::MPH_MAX)/GameConfiguration->CapsSystem.fps;
	
	if(car) delete car;		//delete if already exists
	car=new Animation(GameLibrary,"player");	//get graphics for the car
	if(Configuration::GlobalErrorString!="")
		GameFramework->AbortSystem("animation 'player' could not be found for setupcar");

	//get size of car and the positions to show it
	car->GetStandardDimensions(&carwidth, &carheight);
	//car is always in the centre of the screen, so we already know
	//what bit of the map is being shown, so we get the centre of that
	//as it is shown on the screen
	screenx=((ShownMapWidth/2)+ScreenXOffset)-(carwidth/2);
	screeny=((ShownMapHeight/2)+ScreenYOffset)-(carheight/2);
	//what the car is doing
	Move=CAR_FREEWHEELING;
	Direction=CAR_STRAIGHT;
}

Car::~Car()
{
	if(car) delete car;
}

bool Car::NextMove()
{	
	//initialise fuel_unit as cannot use static in static as may not be initialised
	if(FuelUnit==0) 
		FuelUnit=(1.0/(float)Car::MPH_MAX)/GameConfiguration->CapsSystem.fps;
	
	//update the car. called every frame
	//default to no acceleration pressed
	//and if simple acceleration then car will decrease when no acceleration
	acc=0;
	if(SimpleAcceleration) decel=0.05;
	Handbrake=false;
	
	Move=CAR_FREEWHEELING;
	Direction=CAR_STRAIGHT;
	
	//key presses
	//order is important
	if(keypressed())
	{
		if(key[KEY_LSHIFT]) Handbrake=true;
		if(key[KEY_ESC])
		{
			clear_keybuf();
			return true;
		}
		//brake takes precedence
		if(key[KEY_DOWN]) 
		{
			if(!SimpleAcceleration && decel==0) decel=0.05;
			Move=CAR_DECELERATE;
		}
		else
		{
			if(!SimpleAcceleration) decel=0.0;
			if(key[KEY_UP]) Move=CAR_ACCELERATE;
		}
		if(key[KEY_RIGHT]) Direction=CAR_RIGHT;
		if(key[KEY_LEFT]) Direction=CAR_LEFT;
	}
	
	//handle movement
	if(Move==CAR_ACCELERATE)
		Accelerate();
	else if(Move==CAR_DECELERATE)
		Decelerate();

	//handle direction
	if(Direction==CAR_LEFT)
		TurnLeft();
	else if(Direction==CAR_RIGHT)
		TurnRight();

	//handbrake increases the braking rate and stops any acceleration
	if(Handbrake)
	{
		decel=0.3;
		acc=0;
	}
	
	//adjust speed
	//two types available
	//	simple is the one used in previous examples and goes up/down as accelerate/decelerate
	//	the other acts like a 'cruise control'. This is so that you can fix the speed of 
	//	the car at any RPM - for the temperature/fuel usage gauges
	if(!this->SimpleAcceleration)
		//due to rounding problems of tiny fractions stop the car if we know it has stopped
		if(acc==0 && vel>=0) 
		{
			decel=0;
			vel=0;
		}
		
		//adjust speed based on acceleration/deceleration
		vel=vel-(acc*0.1)+decel;
	
		//maximum or minimum velocity reached
		if(vel<-Car::MAXVEL) vel=-Car::MAXVEL;
		if(vel>Car::MAXVEL) vel=Car::MAXVEL;
	else
	{
		//standard way is to only adjust if necessary to
		//create the effect of a constant cruise control
		if(vel<-Car::MAXVEL) vel=-Car::MAXVEL;
		if(vel>0) vel=0;
	}

	//set cars statistics
	Mph=(int)fabs(vel*Car::MPH_INCREASE);
	if(MilesDriven<999999.0) MilesDriven+=((float)Mph/10.0)/GameConfiguration->CapsSystem.fps;

	//temperature is 100 units, increase by 3.33 per second when redlining
	//if mph in yellow, temperature stays the same
	//if in green, temperature decreases
	if((Mph)>Car::MPH_REDLINE) 
		Temperature+=TemperatureInc;
	else if((Mph)<Car::MPH_YELLOWLINE) 
		Temperature-=TemperatureDec;

	if(Temperature>=100) Temperature=100;
	if(Temperature<0) Temperature=0;
	//fuel - idling doesn't use up much fuel (i.e. pretend 0.5 mph
	Fuel-=(Mph+0.5)*FuelUnit;
	if(Fuel<0) Fuel=0;

	//check for edge bounces - there must be a better way!
	//left or top 
	if (mapx-(ShownMapWidth/2) < 0) 
	{
		if(angle>270)	//upleft
			angle=360-angle;
		else
			angle=180-(angle-180);
	}
	if (mapy-(ShownMapHeight/2) < 0) 
	{
		if(angle>270)	//upleft
			angle=360+180-angle;
		else
			angle=90+(90-angle);	//downleft
	}
	//right or bottom
	if (mapx+(ShownMapWidth/2) > mapblockwidth*mapwidth-16) 
	{
		if(angle<89)	//upleft
			angle=360-angle;
		else
			angle=180+(180-angle);	//downright
	}
	if (mapy+(ShownMapHeight/2) > mapblockheight*mapheight-1) 
	{
		if(angle<179)	//downright
			angle=180-angle;
		else
			angle=360-(angle-180);//downleft
	}


	//get movement for car
	xdistancemoved=sin((float)(angle*PI_Conv))*vel;
	ydistancemoved=cos((float)(angle*PI_Conv))*vel;

	//set map offset as being that of the player car
	this->mapx-=xdistancemoved;
	this->mapy+=ydistancemoved;
	return false;
}

void Car::Accelerate()
{
	static bool TemperatureLatch=false;
	//can always accelerate upto 100mph if fuel is available
	//can accelerate if temperature<100
	//but can only start again when it reaches 75
	if(Fuel && 
		(Mph<100 
		 || (Temperature<100 && !TemperatureLatch) 
		 || (Temperature<75 && TemperatureLatch)
		)
	)
	{
		if(Temperature<75) TemperatureLatch=false;
		if(SimpleAcceleration)
			vel-=0.1;
		else
		{
			decel=0;
			acc=1;
		}
	}
	else
		TemperatureLatch=true;
}

void Car::Decelerate()
{
	if(SimpleAcceleration)
		vel+=0.1;
	else
		decel*=1.1;
}

void Car::TurnLeft()
{
	static int counter=1;

	counter++;
	if(counter>2)
	{
		angle-=9;
		if(angle<0) 
			angle=360+angle;
		counter=0;
	}
	//if not accelerating and turning then apply a bit of deceleration
	if(acc==0) decel*=2;
}

void Car::TurnRight()
{
	static int counter=1;

	counter++;
	if(counter>2)
	{
		angle+=9;
		if(angle>359) angle=360-angle;
		counter=0;
	}
	//if not accelerating and turning then apply a bit of deceleration
	if(acc==0) decel*=2;	
}
